#region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Text;

using Osh;
using Osh.Configuration;
using Osh.Media;

namespace OshHost
{
	public class ScreenChangeArgs : EventArgs
	{
		private string _screenName;

		public ScreenChangeArgs(string screenName)
		{
			_screenName = screenName;
		}

		public string ScreenName
		{
			get { return _screenName; }
		}
	}

	public class OshApplication : IOsh
	{
		private Rectangle _bounds;
		private int _transparentColor;
		private string _mode;
		private MediaController _mediaController;
		private OshSource _oshSource;
		private OshCommandParser _commandParser;

		public OshApplication()
		{
			_mediaController = new MediaController(this);
			InitializeMode();

			_commandParser = new OshCommandParser();
			_commandParser.Command += new EventHandler<OshCommandEventArgs>(OshCommandParser_Command);
		}

		private void InitializeMode()
		{
			if(Configuration.Global.Osh.Modes.Count < 1)
				throw new ConfigurationErrorsException(
					"At least one OSH mode has to be specified in the configuration file.");

			ModeElement modeElement = Configuration.Global.Osh.Modes.GetMode(0);
			this.Mode = modeElement.Name;
		}

		#region IOsh Members
		public bool Visible
		{
			get
			{
				if(_oshSource == null)
					return false;

				return _oshSource.Visible;
			}
			set
			{
				if(_oshSource == null)
					return;

				if(_oshSource.Visible == value)
					return;

				_oshSource.Visible = value;
				if(value)
					OnShow();
				else
					OnHide();
			}
		}

		public int Left
		{
			get { return _bounds.Left; }
		}

		public int Top
		{
			get { return _bounds.Top; }
		}

		public int Width
		{
			get { return _bounds.Width; }
		}

		public int Height
		{
			get { return _bounds.Height; }
		}

		public int TransparentColor
		{
			get { return _transparentColor; }
			set { _transparentColor = value; }
		}

		public string Mode
		{
			get { return _mode; }
			set
			{
				if(_mode == value)
					return;

				Log.Info(Configuration.Local.OshTraceSource, "Changing OSH mode to '{0}'...", value);
				if(!OnModeChanging(value))
					return;

				_mode = value;

				OnModeChanged(value);
			}
		}

		public MediaController MediaController
		{
			get { return _mediaController; }
		}

		public void SetBounds(int x, int y, int width, int height)
		{
			if(_bounds.Left != x || _bounds.Top != y || _bounds.Width != width || _bounds.Height != height)
			{
				_bounds = new Rectangle(x, y, width, height);
				OnBoundsChanged();
			}
		}

		public void Refresh()
		{
			if(_oshSource != null)
				_oshSource.Refresh();
		}

		public event EventHandler Show;
		public event EventHandler Hide;
		public event EventHandler BoundsChanged;
		public event EventHandler<ModeChangeEventArgs> ModeChanging;
		public event EventHandler<ModeChangeEventArgs> ModeChanged;
		public event EventHandler<OshCommandEventArgs> Command;
		#endregion IOsh Members

		public Point Location
		{
			get { return new Point(this.Left, this.Top); }
		}

		public Size Size
		{
			get { return new Size(this.Width, this.Height); }
		}

		internal OshSource OshSource
		{
			get { return _oshSource; }
			set { _oshSource = value; }
		}

		/// <summary>
		/// Notifies managed objects about OSH screen change. Called from script.
		/// </summary>
		/// <param name="screenName"></param>
		public void NotifyScreenChange(string screenName)
		{
			if(this.ScreenChange != null)
				this.ScreenChange(this, new ScreenChangeArgs(screenName));
		}

		private void OnShow()
		{
			Log.Info(Configuration.Local.OshTraceSource, "Osh.OnShow()");
			if(this.Show != null)
				this.Show(this, null);
		}

		private void OnHide()
		{
			Log.Info(Configuration.Local.OshTraceSource, "Osh.OnHide()");
			if(this.Hide != null)
				this.Hide(this, null);
		}

		private void OnCommand(string command)
		{
			Log.Info(Configuration.Local.OshTraceSource, "Osh.OnCommand('{0}')", command);
			if(this.Command != null)
				this.Command(this, new OshCommandEventArgs(command));
		}

		private void OnBoundsChanged()
		{
			Log.Info(Configuration.Local.OshTraceSource,
				"Osh.OnBoundsChanged({0}, {1}, {2}, {3})", this.Left, this.Top, this.Width, this.Height);

			if(_oshSource != null)
				_oshSource.WebBrowser.SetBounds(this.Left, this.Top, this.Width, this.Height);

			if(this.BoundsChanged != null)
				this.BoundsChanged(this, null);
		}

		private bool OnModeChanging(string mode)
		{
			Log.Info(Configuration.Local.OshTraceSource, "Osh.OnModeChanging('{0}')", mode);

			ModeChangeEventArgs args = new ModeChangeEventArgs(mode, true);
			
			if(this.ModeChanging != null)
				this.ModeChanging(this, args);

			return !args.Cancel;
		}

		private void OnModeChanged(string mode)
		{
			Log.Info(Configuration.Local.OshTraceSource, "Osh.OnModeChanged('{0}')", mode);

			if(this.ModeChanged != null)
				this.ModeChanged(this, new ModeChangeEventArgs(mode, false));
		}

		private void OshCommandParser_Command(object sender, OshCommandEventArgs e)
		{
			OnCommand(e.Command);
		}

		public event EventHandler<ScreenChangeArgs> ScreenChange;
	}
}
